package com.example.edu.Service.Impl;

import cn.hutool.json.JSONUtil;
import com.example.edu.Dto.*;
import com.example.edu.Entity.*;
import com.example.edu.Mapper.*;
import com.example.edu.Service.DocumentService;
import com.example.edu.Utils.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhipu.oapi.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.example.edu.Utils.MyConstants.*;

@Service
public class DocumentServiceImpl implements DocumentService {
    @Resource
    DocumentMapper documentMapper;
    @Resource
    UserMapper userMapper;
    @Resource
    TagMapper tagMapper;
    @Resource
    Tag_DocMapper tagDocMapper;
    @Resource
    Picture_DocMapper pictureDocMapper;
    @Resource
    CourseMapper courseMapper;
    @Resource
    Course_RecordMapper course_recordMapper;
    @Resource
    Mind_MapMapper mind_mapMapper;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    CacheDataBaseUtil cacheDataBaseUtil;
    @Resource
    EmbeddingUtil embeddingUtil;
    @Resource
    EsUtil esUtil;
    @Resource
    AIUtil aiUtil;
    private final ObjectMapper objectMapper = new ObjectMapper();
    private static final Logger logger = LoggerFactory.getLogger(DocumentServiceImpl.class);

    @Override
    @Transactional
    public Return create(HttpServletRequest httpServletRequest, DocumentCreateDtoG documentCreateData) {
        try {
            return create1(httpServletRequest,documentCreateData);
        } catch (Exception e){
            return Return.fail(1,"操作失败");
        }
    }

    @Override
    public Return getList(HttpServletRequest httpServletRequest, DocumentListDtoG documentListData) {
        String token = JwtUtil.getTokenFromCookie(httpServletRequest);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        int start_index = documentListData.getStart_index();
        int count = documentListData.getCount();
        int course_id = documentListData.getCourse_id();
        int chapter_id = documentListData.getDocument_chapter();
        OptionDto optionDto = documentListData.getOptions();
        if(optionDto == null)
            return returnNoOptionsList(course_id,chapter_id,start_index,count,user);
        return returnOptionsList(course_id,chapter_id,optionDto,start_index,count,user);
    }

    @Override
    public Return getTags(HttpServletRequest httpServletRequest, TagListDtoG tagListData) {
        String token = JwtUtil.getTokenFromCookie(httpServletRequest);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        List<Tag> tags;
        if(user.getRole() == 0)
            tags = tagMapper.getTags(tagListData.getCourse_id(), tagListData.getChapter_id());
        else
            tags = tagMapper.getTagsS(tagListData.getCourse_id(),tagListData.getChapter_id(),user.getUser_id());
        if (tags == null || tags.isEmpty())
            return Return.ok("null", new ArrayList<>());
        return Return.ok(tags);
    }

    @Override
    public Return get(HttpServletRequest httpServletRequest, int documentId) {
        String token = JwtUtil.getTokenFromCookie(httpServletRequest);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        DocumentListDtoR documentMessage = documentMapper.getDocumentMessage(documentId);
        documentMessage.setTag_names(tagMapper.getTagNamesByDoc(documentId));
        if(user.getRole() == 0) {
            documentMessage.setOperate_right(0);
            return Return.ok(documentMessage);
        }
        if(user.getUser_id() == documentMessage.getDocument_user())
            documentMessage.setOperate_right(0);
        else
            documentMessage.setOperate_right(1);
        return Return.ok(documentMessage);
    }

    @Override
    public ResponseEntity<org.springframework.core.io.Resource> getDocument(Document document) {
        String path = DOCUMENT_ADDRESS + "/" + document.getDocument_id() + "." + document.getDocument_type();
        File file = new File(path);
        if (!file.exists())
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        org.springframework.core.io.Resource resource = new FileSystemResource(file);
        // 设置 HTTP 头，确保前端能正确下载
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName());
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
        addView(document.getDocument_id());
        return ResponseEntity.ok()
                .headers(headers)
                .contentLength(file.length())
                .body(resource);
    }

    @Override
    @Transactional
    public Return delete(Document document) {
        try {
            return delete1(document);
        } catch (Exception e){
            return Return.fail(1,"删除失败");
        }
    }

    @Override
    @Transactional
    public Return update(DocumentUpdateDtoG documentUpdateData) {
        try {
            return update1(documentUpdateData);
        } catch (Exception e){
            return Return.fail(1,"修改失败");
        }
    }

    @Override
    @Transactional
    public Return save(HttpServletRequest httpServletRequest, int documentId, String documentType, String pictureUrls, MultipartFile documentBody) {
        try {
            return save1(httpServletRequest,documentId,documentType,pictureUrls,documentBody);
        } catch (Exception e){
            return Return.fail(1,"保存失败");
        }
    }

    @Override
    public Return getStudent(HttpServletRequest httpServletRequest, Integer courseId) {
        String token = JwtUtil.getTokenFromCookie(httpServletRequest);
        String telephone = JwtUtil.getUsername(token);
        User userId = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        List<User> classmates = userMapper.getClassmates(courseId,userId.getUser_id());
        return Return.ok(classmates);
    }

    @Override
    public Return viewTime(HttpServletRequest httpServletRequest, ViewTimeDtoG viewTimeData) {
        String token = JwtUtil.getTokenFromCookie(httpServletRequest);
        String telephone = JwtUtil.getUsername(token);
        User userId = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        if(userId.getRole() == 0)
            return Return.ok();
        int course_id = documentMapper.getCourse(viewTimeData.getDocument_id());
        Calendar calendar = Calendar.getInstance();
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        course_recordMapper.addTime(course_id,userId.getUser_id(),dayOfWeek-1, viewTimeData.getTime());
        return Return.ok();
    }

    @Override
    public Return like(Integer documentId) {
        return addData(documentId, LIKE_KEY);
    }

    @Override
    public Return collect(Integer documentId) {
        return addData(documentId, COLLECT_KEY);
    }

    @Transactional(rollbackFor = Exception.class)
    public Return create1(HttpServletRequest httpServletRequest, DocumentCreateDtoG documentCreateData) {
        String token = JwtUtil.getTokenFromCookie(httpServletRequest);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        Document document = generateDocument(documentCreateData, user);
        List<Tag> tagList = generateTags(documentCreateData);
        try{
            documentMapper.addDocument(document);
            if(!tagList.isEmpty()) {
                tagList = getExistAndInsertNot(tagList);
                tagDocMapper.addMany(document.getDocument_id(), tagList);
            }
            if(document.getDocument_type().equals("mindMap")){
                Mind_Map mindMap = new Mind_Map(document.getDocument_id(),-1,"");
                mind_mapMapper.addMap(mindMap);
                return Return.ok();
            }
            String path = DOCUMENT_ADDRESS + "/" + document.getDocument_id() + "." + documentCreateData.getDocument_type();
            if(documentCreateData.getDocument_body() != null) {
                MultipartFile document_body = documentCreateData.getDocument_body();
                document_body.transferTo(new File(path));
                if(user.getRole() == 1)
                    addLength(document_body.getSize(),documentCreateData.getDocument_course(),user.getUser_id());
            }
            else {
                File file = new File(path);
                file.createNewFile();
            }
            return Return.ok();
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }



    @Transactional
    public Return delete1(Document document) {
        int document_id = document.getDocument_id();
        String path = DOCUMENT_ADDRESS + "/" + document_id + "." + document.getDocument_type();
        File file = new File(path);
        try{
            documentMapper.deleteDocument(document_id);
            tagDocMapper.delete(document_id);
            pictureDocMapper.delete(document_id);
            if(document.getDocument_type().equals("mindMap")){
                mind_mapMapper.deleteMap(document_id);
                return Return.ok();
            }
            if (file.exists())
                file.delete();
            return Return.ok();
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    @Transactional
    public Return update1(DocumentUpdateDtoG documentUpdateData){
        Document new_document = new Document(documentUpdateData.getDocument_id(), documentUpdateData.getDocument_name(),
                null,-1,documentUpdateData.getDocument_course(),documentUpdateData.getDocument_chapter(),
                documentUpdateData.getRight(),documentUpdateData.getIs_outline(),documentUpdateData.getIs_plan(),null,0,0,0);
        try {
            documentMapper.updateDocument(new_document);
            tagDocMapper.delete(documentUpdateData.getDocument_id());
            List<String> tag_names = documentUpdateData.getTag_names();
            if(tag_names != null && !tag_names.isEmpty()){
                HashSet<String> tag_name_set = new HashSet<>(tag_names);
                tag_names = new ArrayList<>(tag_name_set);
                List<Tag> tagList = new ArrayList<>();
                for(String tag_name : tag_names)
                    tagList.add(new Tag(tag_name));
                tagList = getExistAndInsertNot(tagList);
                tagDocMapper.addMany(documentUpdateData.getDocument_id(), tagList);
            }
            return Return.ok();
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    @Transactional
    public Return save1(HttpServletRequest httpServletRequest, int documentId, String documentType, String pictureUrls, MultipartFile documentBody){
        String path = DOCUMENT_ADDRESS + "/" + documentId + "." + documentType;
        File file = new File(path);
        try {
            List<String> picture_urls = objectMapper.readValue(pictureUrls, new TypeReference<List<String>>() {});
            List<Integer> picture_ids = new ArrayList<>();
            for (String url : picture_urls)
                picture_ids.add(RegexUtils.getPictureId(url));
            pictureDocMapper.delete(documentId);
            if(!picture_ids.isEmpty())
                pictureDocMapper.addMany(documentId,picture_ids);
            documentBody.transferTo(file);
            addLength(documentBody.getSize(),httpServletRequest,documentId);
            addLastSaveTime(documentId);
            return Return.ok();
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    private Return addData(Integer documentId, String preKey) {
        String key = preKey + documentId;
        String data = stringRedisTemplate.opsForValue().get(key);
        if (data == null){
            stringRedisTemplate.opsForValue().set(key,"1");
            return Return.ok();
        }
        stringRedisTemplate.opsForValue().set(key,String.valueOf(Integer.parseInt(data)+1));
        return Return.ok();
    }

    private List<Tag> getExistAndInsertNot(List<Tag> tags){
        try {
            List<Tag> in_cache = new ArrayList<>();
            List<Tag> not_in_cache = new ArrayList<>();
            for (Tag tag : tags) {
                String key = TAG_KEY + tag.getTag_name();
                String tagId = stringRedisTemplate.opsForValue().get(key);
                if (tagId != null) {
                    tag.setTag_id(Integer.parseInt(tagId));
                    in_cache.add(tag);
                } else
                    not_in_cache.add(tag);
            }
            if(!not_in_cache.isEmpty())
                not_in_cache = insertNotExist(not_in_cache);
            for (Tag tag : not_in_cache) {
                String key = TAG_KEY + tag.getTag_name();
                stringRedisTemplate.opsForValue().set(key, String.valueOf(tag.getTag_id()), TAG_CACHE_TTL, TimeUnit.HOURS);
            }
            in_cache.addAll(not_in_cache);
            return in_cache;
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    private List<Tag> insertNotExist(List<Tag> tags){
        List<Tag> exist = tagMapper.selectExist(tags);
        HashSet<String> exist_names = new HashSet<>();
        for (Tag e_tag : exist)
            exist_names.add(e_tag.getTag_name());
        List<Tag> notExist = new ArrayList<>();
        for(Tag tag : tags){
            if(!exist_names.contains(tag.getTag_name()))
                notExist.add(tag);
        }
        if(!notExist.isEmpty())
            tagMapper.insertMany(notExist);
        exist.addAll(notExist);
        return exist;
    }

    private Document generateDocument(DocumentCreateDtoG documentCreateData, User user) {
        if(user.getRole() == 0)
            return new Document(0, documentCreateData.getDocument_name(), documentCreateData.getDocument_type(),
                    user.getUser_id(), documentCreateData.getDocument_course(), documentCreateData.getDocument_chapter(),
                    documentCreateData.getRight(), documentCreateData.getIs_outline(), documentCreateData.getIs_plan(), null,0,0,0);
        else
            return new Document(0, documentCreateData.getDocument_name(), documentCreateData.getDocument_type(),
                    user.getUser_id(), documentCreateData.getDocument_course(), documentCreateData.getDocument_chapter(),
                    documentCreateData.getRight(), 0, 0, null,0,0,0);
    }

    private List<Tag> generateTags(DocumentCreateDtoG documentCreateData){
        List<String> tag_names = documentCreateData.getTag_names();
        List<Tag> tagList = new ArrayList<>();
        if(tag_names!= null && !tag_names.isEmpty()) {
            HashSet<String> tag_name_set = new HashSet<>(tag_names);
            tag_names = new ArrayList<>(tag_name_set);
            for (String tag_name : tag_names)
                tagList.add(new Tag(tag_name));
        }
        return tagList;
    }

    private Return returnNoOptionsList(int course_id,int chapter_id,int start_index,int count,User user){
        if(user.getRole() == 0) {
            int total = documentMapper.getDocumentNum(course_id, chapter_id);
            if (total == 0)
                return Return.ok("还没有文档",new ListDtoR(total, new ArrayList<>()));
            List<DocumentListDtoR> documents = documentMapper.getDocuments(course_id, chapter_id, start_index, Math.min(count, total - start_index));
            if (documents == null || documents.isEmpty())
                return Return.ok("这一页还没有文档",new ListDtoR(total, new ArrayList<>()));
            for (DocumentListDtoR document : documents) {
                document.setTag_names(tagMapper.getTagNamesByDoc(document.getDocument_id()));
                document.setOperate_right(0);
            }
            return Return.ok(new ListDtoR(total, documents));
        }
        else {
            int total = documentMapper.getDocumentNumS(course_id,chapter_id,user.getUser_id());
            if (total == 0)
                return Return.ok("还没有文档",new ListDtoR(total, new ArrayList<>()));
            List<DocumentListDtoR> documents = documentMapper.getDocumentsS(course_id, chapter_id, user.getUser_id(),start_index, Math.min(count, total - start_index));
            if (documents == null || documents.isEmpty())
                return Return.ok("这一页还没有文档",new ListDtoR(total, new ArrayList<>()));
            for (DocumentListDtoR document : documents) {
                document.setTag_names(tagMapper.getTagNamesByDoc(document.getDocument_id()));
                if(document.getDocument_user() == user.getUser_id())
                    document.setOperate_right(0);
                else
                    document.setOperate_right(1);
            }
            return Return.ok(new ListDtoR(total, documents));
        }
    }

    private Return returnOptionsList(int course_id,int chapter_id,OptionDto optionDto,int start_index,int count,User user){
        List<DocumentListDtoR> documents;
        if(user.getRole() == 0) {
            documents = documentMapper.getDocuments2(course_id, chapter_id, optionDto);
            if (documents == null || documents.isEmpty())
                return Return.ok("还没有文档",new ListDtoR(0, new ArrayList<>()));
            for (DocumentListDtoR document : documents) {
                setDoc_Tag(document);
                document.setOperate_right(0);
            }
        } else {
            documents = documentMapper.getDocuments2S(course_id, chapter_id, optionDto, user.getUser_id());
            if (documents == null || documents.isEmpty())
                return Return.ok("还没有文档",new ListDtoR(0, new ArrayList<>()));
            for (DocumentListDtoR document : documents) {
                setDoc_Tag(document);
                if(document.getDocument_user() == user.getUser_id())
                    document.setOperate_right(0);
                else
                    document.setOperate_right(1);
            }
        }
        return filterByTags(optionDto,documents,start_index,count);
    }

    private void setDoc_Tag(DocumentListDtoR document) {
        List<Tag> doc_tags = tagMapper.getTagByDoc(document.getDocument_id());
        List<Integer> tag_ids = new ArrayList<>();
        List<String> tag_names = new ArrayList<>();
        for (Tag doc_tag : doc_tags){
            tag_ids.add(doc_tag.getTag_id());
            tag_names.add(doc_tag.getTag_name());
        }
        document.setTag_ids(tag_ids);
        document.setTag_names(tag_names);
    }

    private Return filterByTags(OptionDto optionDto, List<DocumentListDtoR> documents, int start_index, int count) {
        if (optionDto.getTag_ids() == null || optionDto.getTag_ids().isEmpty()) {
            return getaReturn(start_index, count, documents);
        }
        List<DocumentListDtoR> documents2 = new ArrayList<>();
        for (DocumentListDtoR document : documents) {
            if (new HashSet<>(document.getTag_ids()).containsAll(optionDto.getTag_ids()))
                documents2.add(document);
        }
        if (documents2.isEmpty())
            return Return.ok("还没有文档",new ListDtoR(0, new ArrayList<>()));
        return getaReturn(start_index, count, documents2);
    }

    private Return getaReturn(int start_index, int count, List<DocumentListDtoR> documents) {
        int size = documents.size();
        if(start_index >= size)
            return Return.ok("这一页还没有文档",new ListDtoR(size, new ArrayList<>()));
        List<DocumentListDtoR> sub_documents = documents.subList(start_index, Math.min(start_index + count,size));
        if (sub_documents.isEmpty())
            return Return.ok("这一页还没有文档",new ListDtoR(documents.size(), new ArrayList<>()));
        return Return.ok(new ListDtoR(documents.size(), sub_documents));
    }

    @Async
    protected void addLength(long length,int course_id,int student_id){
        Calendar calendar = Calendar.getInstance();
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        try{
            course_recordMapper.addWords(course_id,student_id,dayOfWeek-1,(int) length);
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    @Async
    protected void addLength(long length,HttpServletRequest httpServletRequest,int document_id){
        String token = JwtUtil.getTokenFromCookie(httpServletRequest);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        if(user.getRole() == 0)
            return;
        Document document = documentMapper.getAuthorAndCourse(document_id);
        addLength(length,document.getDocument_course(),document.getDocument_user());
    }

    @Async
    protected void addLastSaveTime(int document_id){
        String key = LAST_SAVE_KEY + document_id;
        long timestamp = System.currentTimeMillis();
        stringRedisTemplate.opsForValue().set(key, Long.toString(timestamp),LAST_SAVE_TTL,TimeUnit.MINUTES);
    }

    @Async
    protected void addView(int document_id){
        String key = VIEW_KEY + document_id;
        String view = stringRedisTemplate.opsForValue().get(key);
        if(view == null) {
            stringRedisTemplate.opsForValue().set(key, "1");
            return;
        }
        stringRedisTemplate.opsForValue().set(key,String.valueOf(Integer.parseInt(view)+1));
    }

    @Async
    protected void extractText(MultipartFile document_body, Document document) {
        String contentType = document_body.getContentType();
        String textContent = null;

        // 处理 .docx
        if ("application/vnd.openxmlformats-officedocument.wordprocessingml.document".equals(contentType))
            textContent = extractTextFromDocx(document_body);
        // 处理 .md 和 .txt
        if ("text/markdown".equals(contentType) || "text/plain".equals(contentType))
            textContent = extractTextFromTxtOrMd(document_body);
        // 处理 .pdf
        if ("application/pdf".equals(contentType))
            textContent = extractTextFromPdf(document_body);

        if (textContent == null)
            return;
        List<String> texts = RegexUtils.splitTextIntoChunks(textContent);
        List<List<Double>> r = embeddingUtil.getEmbeddings(texts);
        List<EsDocument> esDocuments = new ArrayList<>();
        for(int i = 0;i < texts.size();i++)
            esDocuments.add(new EsDocument(document.getDocument_id(),i,document.getDocument_name(),document.getDocument_course(),document.getDocument_chapter(),
                    document.getIs_outline(),document.getIs_plan(),texts.get(i),r.get(i)));
        esUtil.insertDocuments(esDocuments);
    }

    // 解析 docx
    private static String extractTextFromDocx(MultipartFile file) {
        try (InputStream is = file.getInputStream(); XWPFDocument doc = new XWPFDocument(is)) {
            XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
            return extractor.getText();
        } catch (IOException e){
            logger.error(e.getMessage());
            return null;
        }
    }

    // 解析 md 或 txt
    private static String extractTextFromTxtOrMd(MultipartFile file) {
        StringBuilder sb = new StringBuilder();
        try (Scanner scanner = new Scanner(file.getInputStream(), String.valueOf(StandardCharsets.UTF_8))) {
            while (scanner.hasNextLine()) {
                sb.append(scanner.nextLine()).append("\n");
            }
            return sb.toString();
        } catch (IOException e){
            logger.error(e.getMessage());
            return null;
        }
    }

    // 解析 pdf
    private static String extractTextFromPdf(MultipartFile file) {
        try (InputStream is = file.getInputStream(); PDDocument document = PDDocument.load(is)) {
            PDFTextStripper stripper = new PDFTextStripper();
            return stripper.getText(document);
        } catch (IOException e){
            logger.error(e.getMessage());
            return null;
        }
    }

    @Async
    @Transactional
    public void autoCreatePlan(int userId, int courseId) {
        if(documentMapper.getPlan(courseId) > 0 || !cacheDataBaseUtil.tryLock(PLAN_KEY, String.valueOf(courseId),LOCK_TTL,TimeUnit.MINUTES))
            return;
        try{
            String r = getAnswer(courseId, EXAMPLE_PLAN_ADDRESS, EX_PLAN_ADDRESS);
            if(r == null)
                return;
            r = JSONUtil.toJsonStr(JSONUtil.parse(r));
            Document document = new Document(0,"教学方案","json",
                    userId,courseId,-1,1,0,1,null,0,0,0);
            documentMapper.addDocument(document);
            writeStringToFile(r,DOCUMENT_ADDRESS+"/"+document.getDocument_id()+".json");
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        } finally {
            cacheDataBaseUtil.unlock(PLAN_KEY, String.valueOf(courseId));
        }
    }

    @Async
    @Transactional
    public void autoCreateOutline(int userId, int courseId) {
        if(documentMapper.getOutLine(courseId) > 0 || !cacheDataBaseUtil.tryLock(OUTLINE_KEY, String.valueOf(courseId),LOCK_TTL,TimeUnit.MINUTES))
            return;
        try{
            String r = getAnswer(courseId, EXAMPLE_OUTLINE_ADDRESS, EX_OUTLINE_ADDRESS);
            if(r == null)
                return;
            r = JSONUtil.toJsonStr(JSONUtil.parse(r));
            Document document = new Document(0,"教学大纲","json",
                    userId,courseId,-1,1,1,0,null,0,0,0);
            documentMapper.addDocument(document);
            writeStringToFile(r,DOCUMENT_ADDRESS+"/"+document.getDocument_id()+".json");
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        } finally {
            cacheDataBaseUtil.unlock(OUTLINE_KEY, String.valueOf(courseId));
        }
    }

    private String getAnswer(int courseId, String mouldAddress, String exAddress) throws IOException {
        String courseName = courseMapper.getCourseName(courseId);
        String content = readFile(mouldAddress);
        String exContent = readFile(exAddress);
        List<String> userMessages = new ArrayList<>();
        List<String> systemMessages = new ArrayList<>();
        userMessages.add(String.format(AI_AUTO_CREATE_USER_PROMPT,courseName) + content);
        systemMessages.add(String.format(AI_AUTO_CREATE_SYSTEM_PROMPT,courseName,exContent));
        PromptDto prompt_data = new PromptDto(userMessages,systemMessages,courseName);
        return aiUtil.getAnswer(prompt_data,1, AI_MODEL_CODE[0]);
    }

    private String readFile(String path) throws IOException {
        return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
    }

    private void writeStringToFile(String content, String path) throws IOException {
        Files.write(Paths.get(path), content.getBytes(StandardCharsets.UTF_8),
                StandardOpenOption.CREATE,
                StandardOpenOption.TRUNCATE_EXISTING);
    }
}
